import json
import logging
from datetime import datetime
from croniter import croniter
import pytz
from app.models.database import db, Schedule, Scan
from app.services.scan_service import ScanService

logger = logging.getLogger(__name__)

class ScheduleService:
    """任务调度服务，负责处理定时扫描任务的创建、管理和执行"""
    
    @staticmethod
    def validate_cron_expression(expression):
        """验证cron表达式是否有效"""
        try:
            cron = croniter(expression)
            return True, ""
        except Exception as e:
            return False, f"无效的cron表达式: {str(e)}"
    
    @staticmethod
    def create_schedule(name, targets, scan_depth, scan_types, cron_expression):
        """创建新的调度任务"""
        # 验证cron表达式
        is_valid, message = ScheduleService.validate_cron_expression(cron_expression)
        if not is_valid:
            raise ValueError(message)
        
        # 验证并处理参数
        if not name or not name.strip():
            raise ValueError("任务名称不能为空")
        
        if not targets or len(targets) == 0:
            raise ValueError("目标列表不能为空")
        
        if not scan_types or len(scan_types) == 0:
            raise ValueError("扫描类型列表不能为空")
        
        # 计算下一次执行时间
        cron = croniter(cron_expression, datetime.now())
        next_run_at = cron.get_next(datetime)
        
        # 创建调度任务
        schedule = Schedule(
            name=name,
            targets=json.dumps(targets),
            scan_depth=scan_depth,
            scan_types=json.dumps(scan_types),
            cron_expression=cron_expression,
            next_run_at=next_run_at
        )
        
        try:
            db.session.add(schedule)
            db.session.commit()
            logger.info(f"创建调度任务成功: {name}")
            return schedule.to_dict()
        except Exception as e:
            db.session.rollback()
            logger.error(f"创建调度任务失败: {str(e)}")
            raise
    
    @staticmethod
    def get_all_schedules():
        """获取所有调度任务"""
        try:
            schedules = Schedule.query.all()
            return [schedule.to_dict() for schedule in schedules]
        except Exception as e:
            logger.error(f"获取调度任务列表失败: {str(e)}")
            raise
    
    @staticmethod
    def get_schedule_by_id(schedule_id):
        """根据ID获取调度任务"""
        try:
            schedule = Schedule.query.get(schedule_id)
            if not schedule:
                raise ValueError(f"未找到ID为{schedule_id}的调度任务")
            return schedule.to_dict()
        except Exception as e:
            logger.error(f"获取调度任务失败: {str(e)}")
            raise
    
    @staticmethod
    def update_schedule(schedule_id, **kwargs):
        """更新调度任务"""
        try:
            schedule = Schedule.query.get(schedule_id)
            if not schedule:
                raise ValueError(f"未找到ID为{schedule_id}的调度任务")
            
            # 更新任务属性
            if 'name' in kwargs:
                schedule.name = kwargs['name']
            
            if 'targets' in kwargs:
                schedule.targets = json.dumps(kwargs['targets'])
            
            if 'scan_depth' in kwargs:
                schedule.scan_depth = kwargs['scan_depth']
            
            if 'scan_types' in kwargs:
                schedule.scan_types = json.dumps(kwargs['scan_types'])
            
            if 'cron_expression' in kwargs:
                is_valid, message = ScheduleService.validate_cron_expression(kwargs['cron_expression'])
                if not is_valid:
                    raise ValueError(message)
                schedule.cron_expression = kwargs['cron_expression']
                # 重新计算下一次执行时间
                cron = croniter(kwargs['cron_expression'], datetime.now())
                schedule.next_run_at = cron.get_next(datetime)
            
            if 'is_active' in kwargs:
                schedule.is_active = kwargs['is_active']
                # 如果任务被激活，重新计算下一次执行时间
                if kwargs['is_active'] and schedule.cron_expression:
                    cron = croniter(schedule.cron_expression, datetime.now())
                    schedule.next_run_at = cron.get_next(datetime)
            
            schedule.updated_at = datetime.utcnow()
            
            db.session.commit()
            logger.info(f"更新调度任务成功: {schedule_id}")
            return schedule.to_dict()
        except Exception as e:
            db.session.rollback()
            logger.error(f"更新调度任务失败: {str(e)}")
            raise
    
    @staticmethod
    def delete_schedule(schedule_id):
        """删除调度任务"""
        try:
            schedule = Schedule.query.get(schedule_id)
            if not schedule:
                raise ValueError(f"未找到ID为{schedule_id}的调度任务")
            
            db.session.delete(schedule)
            db.session.commit()
            logger.info(f"删除调度任务成功: {schedule_id}")
            return True
        except Exception as e:
            db.session.rollback()
            logger.error(f"删除调度任务失败: {str(e)}")
            raise
    
    @staticmethod
    def check_and_execute_due_schedules():
        """检查并执行到期的调度任务"""
        try:
            now = datetime.now()
            # 查询所有活跃的、且下一次执行时间已到或已过的任务
            schedules = Schedule.query.filter(
                Schedule.is_active == True,
                Schedule.next_run_at <= now
            ).all()
            
            for schedule in schedules:
                logger.info(f"执行调度任务: {schedule.name} (ID: {schedule.id})")
                
                try:
                    # 执行扫描任务
                    targets = json.loads(schedule.targets)
                    scan_types = json.loads(schedule.scan_types)
                    
                    # 为每个目标创建扫描任务
                    for target in targets:
                        scan_data = {
                            'target': target,
                            'scan_depth': schedule.scan_depth,
                            'scan_types': scan_types,
                            'scheduled': True,
                            'schedule_id': schedule.id
                        }
                        scan_service = ScanService()
                        scan_service.submit_scan(scan_data)
                    
                    # 更新任务状态
                    schedule.last_run_at = now
                    # 计算下一次执行时间
                    cron = croniter(schedule.cron_expression, now)
                    schedule.next_run_at = cron.get_next(datetime)
                    schedule.updated_at = now
                    
                except Exception as e:
                    logger.error(f"执行调度任务{schedule.name}失败: {str(e)}")
                    # 即使执行失败，也更新下一次执行时间，避免重复执行
                    try:
                        cron = croniter(schedule.cron_expression, now)
                        schedule.next_run_at = cron.get_next(datetime)
                        schedule.updated_at = now
                    except:
                        pass
            
            db.session.commit()
            return len(schedules)
        except Exception as e:
            db.session.rollback()
            logger.error(f"检查和执行调度任务时发生错误: {str(e)}")
            raise
    
    @staticmethod
    def calculate_next_run_times(schedule_id, count=5):
        """计算调度任务未来的执行时间"""
        try:
            schedule = Schedule.query.get(schedule_id)
            if not schedule:
                raise ValueError(f"未找到ID为{schedule_id}的调度任务")
            
            if not schedule.cron_expression:
                return []
            
            cron = croniter(schedule.cron_expression, datetime.now())
            next_runs = []
            
            for _ in range(count):
                next_run = cron.get_next(datetime)
                next_runs.append(next_run.isoformat())
            
            return next_runs
        except Exception as e:
            logger.error(f"计算调度任务执行时间失败: {str(e)}")
            raise
    
    @staticmethod
    def toggle_schedule_status(schedule_id):
        """切换调度任务的激活状态"""
        try:
            schedule = Schedule.query.get(schedule_id)
            if not schedule:
                raise ValueError(f"未找到ID为{schedule_id}的调度任务")
            
            schedule.is_active = not schedule.is_active
            
            if schedule.is_active and schedule.cron_expression:
                # 如果任务被激活，重新计算下一次执行时间
                cron = croniter(schedule.cron_expression, datetime.now())
                schedule.next_run_at = cron.get_next(datetime)
            
            schedule.updated_at = datetime.utcnow()
            
            db.session.commit()
            logger.info(f"切换调度任务状态成功: {schedule_id}, 新状态: {schedule.is_active}")
            return schedule.to_dict()
        except Exception as e:
            db.session.rollback()
            logger.error(f"切换调度任务状态失败: {str(e)}")
            raise